package models;

import java.util.*;

import javax.persistence.*;

import net.sf.oval.constraint.MaxSize;

import org.apache.commons.mail.EmailException;

import play.data.binding.As;
import play.data.validation.Required;
import play.db.jpa.*;

/**
 * 
 * 
 * Topic Entity, Used to create a Topic
 * 
 * 
 * @author Nesreen elgohary
 */

@Entity
public class Topic extends play.db.jpa.Model {
	/**
	 * The number of times that the system will check whether the topic is
	 * active or not
	 * 
	 * @author Nourhan El-Nehlawy
	 */
	public int notifications = 30;

	/**
	 * The relation is a user can be the author of many topics but topic belongs
	 * to only one user
	 * 
	 * @author Nesreen elGohary
	 */
	@ManyToOne
	public User author;
	/**
	 * The title of the topic
	 * 
	 * @author Nesreen elGohary
	 */
	public String title;
	/**
	 * The body of the contact
	 * 
	 * @author Nesreen elGohary
	 */
	@Lob
	@Required
	@MaxSize(10000)
	public String body;
	/**
	 * The spam status of the content
	 * 
	 * @author Nesreen elGohary
	 */
	public Boolean spam;
	/**
	 * The date when the content was created
	 * 
	 * @author Nesreen elGohary
	 */
	@Required
	@As("yyyy-MM-dd")
	public Date postedAt;
	/**
	 * The relation is a entity can have many topics but topic belongs to only
	 * one entity
	 * 
	 * @author Nesreen elGohary
	 */
	@ManyToOne
	public Entities entity;
	/**
	 * the execution of a topic
	 * 
	 * @author Nesreen elGohary
	 */
	public boolean execution = false;
	/**
	 * the visibility of a topic
	 * 
	 * @author Nesreen elGohary
	 */
	public boolean hide = false;
	/**
	 * The status of the topic
	 * 
	 * @author Nesreen elGohary
	 */
	public String status = " ";
	/**
	 * The rating of the topic
	 * 
	 * @author Nesreen elGohary
	 */
	public int rate = 0;

	/**
	 * The topic locking status
	 * 
	 * @author Nesreen elGohary
	 */
	public boolean isLocked = false;
	/**
	 * The availability of ideas on a topic
	 * 
	 * @author Nesreen elGohary
	 */
	public boolean hasIdeas = false;
	/**
	 * The relation is a topic can have many ideas but ideas can belong to only
	 * one topic
	 * 
	 * @author Nesreen elGohary
	 */
	@OneToMany(mappedBy = "topic", cascade = CascadeType.ALL)
	public List<Idea> ideas;
	/**
	 * The relation can be topic can belong to many tags and tags can have many
	 * topics
	 * 
	 * @author Nesreen elGohary
	 */
	@ManyToMany
	public List<Tag> tags;
	/**
	 * The relation can be topic can have many subscribers and subscribers can
	 * subscribe on many topics
	 * 
	 * @author Nesreen elGohary
	 */
	@ManyToMany
	public List<User> subscribers;
	/**
	 * the topic can have many relations & the every relation includes more tn
	 * one topic
	 * 
	 * @author Menna Magdy
	 */

	@ManyToMany(mappedBy = "topicList")
	public List<Relation> relationList;
	/**
	 * The list of users that marked this topic as spam
	 * 
	 * @author Shahir El Faramawy
	 */
	@JoinTable(name = "TopicSpammers")
	@ManyToMany
	public List<User> spamReporters;

	/**
	 * Default constructor
	 * 
	 * @param body
	 *            the body of the contact
	 * @param author
	 *            the author of the topic
	 * @param entity
	 *            the entity where the topic belongs
	 * @param title
	 *            The title of the topic
	 * 
	 * 
	 * 
	 * @author Nesreen elGohary
	 * @author Mai Selwanes just for logging
	 */
	public Topic(String body, User author, Entities entity, String title) {
		this.body = body;
		this.author = author;
		this.entity = entity;
		this.title = title;
		this.relationList = new ArrayList<Relation>();
		this.subscribers = new ArrayList<User>();
		tags = new ArrayList<Tag>();
		ideas = new LinkedList<Idea>();
		postedAt = new Date();
		this.spamReporters = new ArrayList<User>();
		Log log = new Log(this.entity.organization, author,
				" has created a new Topic of title: " + title + " in Entity: "
						+ entity.entityName);
		log.save();
		this.spam = false;
		subscribers.add(author);
		entity.topics.add(this);
		author.topic.add(this);
		this.status = "active";

	}

	/**
	 * it returns all ideas that are found in the database
	 * 
	 * @author Ghada Abu Shady
	 */
	public List<Idea> getAllIdeas() {
		List<Idea> ideaList = Idea.findAll();
		return (ideaList);
	}

	/**
	 * closes the topic for execution and sends notification about executing
	 * this topic
	 * 
	 * added log statement
	 * 
	 * increased contribution counter of user by 10
	 * 
	 * @author youstina galal
	 * @author Mai Selwanes
	 * @author Heba Khattab
	 */

	public void executeAndPromote(Idea ideaOne) {
		int counterOfTurn = 0;
		for (int kCounter = 0; kCounter < ideas.size(); kCounter++) {
			System.out.println("youstinaSSSSSSS");
			if (ideas.get(kCounter).id == ideaOne.id) {
				System.out.println(ideas.get(kCounter).body);
				List<User> user = new ArrayList<User>();
				ideas.get(kCounter).execution = true;
				ideas.get(kCounter).save();

				System.out.println(ideas.get(kCounter).execution);
				user.add(ideaOne.author);
				String details = ideaOne.author.name + " "
						+ "your idea is executed" + "" + ideaOne.title;
				ideas.get(kCounter).topic.execution = true;
				ideas.get(kCounter).topic.save();
				System.out.println(ideas.get(kCounter).topic.execution);
				this.author.sendNotificationWithType(details, user, "Idea",
						ideaOne.id);
				counterOfTurn++;
			}
		}
		if (counterOfTurn > 0) {
			String detailsOne = this.author.name + " " + "is closed  a topic"
					+ this.title + "because of Idea" + "" + ideaOne.title + ""
					+ "has been executed" + " in entity called"
					+ entity.entityName;

			this.author.sendNotificationWithType(detailsOne, this.subscribers,
					"Topic", this.id);

			author.contributionCounter += 10;
			Log log = new Log(this.entity.organization, this.author,
					" has executed and promoted the topic of title: "
							+ this.title + " in entity "
							+ this.entity.entityName);
			log.save();
		} else {
			return;
		}
	}

	/**
	 * AddUsertoSubscribers is a method tt takes a user as a param and adds it
	 * to the list of subscriber of this topic
	 * 
	 * added log statement
	 * 
	 * @param User
	 *            tt should be added
	 * 
	 * @author Sara Assem
	 * @author Mai Selwanes
	 */
	public void addUserToSubscribers(User user) {

		subscribers.add(user);
		Log log = new Log(this.entity.organization, user,
				" has been added to the list of subscribers of the topic of title: "
						+ this.title + " in entity " + this.entity.entityName);
		log.save();
	}

	/**
	 * RemoveUserFromSubscribers is a method tt takes a user as a param and
	 * removes it from the list of subscriber of this topic
	 * 
	 * added log statement
	 * 
	 * @param User
	 *            tt should be removed
	 * 
	 * @author Sara Assem
	 * @author Mai Selwanes
	 */
	public void removeUserFromSubscribers(User user) {
		subscribers.remove(user);
		Log log = new Log(this.entity.organization, user,
				" has been removed from the list of subscribers of the topic of title: "
						+ this.title + " in entity " + this.entity.entityName);
		log.save();

	}

	/**
	 * Method which takes topic (Topic) as an input to lock it so nobody can
	 * construct a relation with this specific topic and then save the new
	 * values in the database
	 * 
	 * @param Topic
	 *            , The method takes topic object as an input added log
	 *            statement
	 * 
	 * @author Momed Abdel Kader
	 * @author Mai Selwanes
	 */

	public void lockTopic(Topic topic) {

		topic.isLocked = true;
		topic.save();
		Log log = new Log(this.entity.organization, topic.author,
				" has locked the topic of title: " + this.title + " in entity "
						+ this.entity.entityName);
		log.save();

	}

	/**
	 * Method which takes topic Topic as an input to unlock it so anybody can
	 * construct a relation with this specific topic and then save the new
	 * values in the database
	 * 
	 * @param Topic
	 *            , The method takes topic object as an input
	 * 
	 *            added log statement
	 * 
	 * @author Momed Abdel Kader
	 * @author Mai Selwanes
	 * 
	 */

	public void unlockTopic(Topic topic) {

		topic.isLocked = false;
		topic.save();
		Log log = new Log(this.entity.organization, topic.author,
				" has unlocked the topic of title: " + this.title
						+ " in entity " + this.entity.entityName);
		log.save();

	}

	/**
	 * marks a topic as spams by a user. The organizer is then notified via
	 * e-mail and notification. This is also logged
	 * 
	 * @param User
	 *            The user who will spam the topic.
	 * @throws exception
	 *             an email exception
	 * 
	 * @author Shahir El Faramawy
	 * @author Mai Selwanes
	 */
	public void spamTopic(User user) throws EmailException {
		this.spamReporters.add(user);
		user.topicSpammed.add(this);
		this.save();
		user.save();
		String content = " spammed topic" + this.title + "created by"
				+ this.author.name;
		ArrayList<User> subscribers = new ArrayList<User>();
		subscribers.add(this.entity.organization.lead);
		user.sendNotificationWithType(content, subscribers, "Topic", this.id);
		Email mail = new Email(user.email, this.entity.organization.lead.email,
				"Spamming", "" + user.name + " spamed the topic" + this.title
						+ "");
		mail.sendEmail();
		Log log = new Log(this.entity.organization, user,
				" has spammed the topic of title: " + this.title
						+ " in entity " + this.entity.entityName
						+ "created by user named " + this.author + ".");
		log.save();
	}

	/**
	 * this method checks whether the user is in the subscriber list of the
	 * topic or not
	 * 
	 * 
	 * @param User
	 *            user the user tt we want to check is he in the list or not .
	 * @author Sara Assem
	 */
	public boolean userinSubscribers(User user) {

		for (User subscriber : this.subscribers) {
			if (user == subscriber)
				return true;

		}
		return false;
	}

	public List<Relation> getTopics() {
		return this.relationList;
	}

	/**
	 * creates and return the idea that has been added to the topic. it notifies
	 * the subscribers to the topic with the new idea added.
	 * 
	 * added log statement
	 * 
	 * @param author
	 *            the creator of the idea
	 * 
	 * @param title
	 *            title of the new idea
	 * 
	 * @param body
	 *            body of the new idea
	 * @return idea the idea that has been created
	 * 
	 * @author Ghada Abu Shady
	 * @author Mai Selwanes
	 * @author Heba khattab incrementing user contribution counter by 1
	 */
	public Idea addIdeaToTopic(User author, String title, String body) {

		Idea idea = new Idea(author, title, body, this).save();
		this.ideas.add(idea);
		String notificationBody = "" + author.name
				+ " has published the idea named " + title + "in the topic "
				+ idea.topic.title;
		author.sendNotificationWithType(notificationBody, this.subscribers,
				"Idea", idea.id);

		for (int x = 0; x < this.tags.size(); x++) {
			author.sendNotificationWithType(body, this.tags.get(x).subscribers,
					"Idea", idea.id);
		}
		this.subscribers.add(author);

		author.contributionCounter++;
		Log log = new Log(this.entity.organization, author,
				" has added an idea of title: " + title + "to topic of title: "
						+ this.title + " in entity " + this.entity.entityName);
		log.save();
		this.save();
		return idea;
	}

	/**
	 * This method takes the user id and returns true if the type of the
	 * organization that he's searching in is public , OR False if the
	 * organization is private/Closed and he's Not a member in it and True
	 * otherwise
	 * 
	 * @param userId
	 *            the user's id
	 * @return returns the True or False
	 * @author Nourhan El-Nehlawy
	 */
	public boolean checkType(Long userId) {
		if (!this.entity.organization.type.matches("Public")) {
			User user = User.findById(userId);
			if (this.entity.organization.checkUser(user)) {
				return true;
			} else
				return false;
		} else
			return true;
	}

}
